/*******************************************************************************
* Copyright (c) 2015-2017
* School of Electrical, Computer and Energy Engineering, Arizona State University
* PI: Prof. Shimeng Yu
* All rights reserved.
* 
* This source code is part of NeuroSim - a device-circuit-algorithm framework to benchmark 
* neuro-inspired architectures with synaptic devices(e.g., SRAM and emerging non-volatile memory). 
* Copyright of the model is maintained by the developers, and the model is distributed under 
* the terms of the Creative Commons Attribution-NonCommercial 4.0 International Public License 
* http://creativecommons.org/licenses/by-nc/4.0/legalcode.
* The source code is free and you can redistribute and/or modify it
* by providing that the following conditions are met:
* 
*  1) Redistributions of source code must retain the above copyright notice,
*     this list of conditions and the following disclaimer.
* 
*  2) Redistributions in binary form must reproduce the above copyright notice,
*     this list of conditions and the following disclaimer in the documentation
*     and/or other materials provided with the distribution.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* 
* Developer list: 
*   Pai-Yu Chen	    Email: pchen72 at asu dot edu 
*                    
*   Xiaochen Peng   Email: xpeng15 at asu dot edu
********************************************************************************/

#include <cmath>
#include <iostream>
#include "constant.h"
#include "formula.h"
#include "VoltageSenseAmp.h"

using namespace std;

VoltageSenseAmp::VoltageSenseAmp(const InputParameter& _inputParameter, const Technology& _tech, const MemCell& _cell): inputParameter(_inputParameter), tech(_tech), cell(_cell), FunctionUnit() {
	initialized = false;
}

void VoltageSenseAmp::Initialize(int _numReadCol, double _clkFreq) {
	if (initialized)
		cout << "[VoltageSenseAmp] Warning: Already initialized!" << endl;
	
	voltageSenseDiff = 0.1;
	numReadCol = _numReadCol;
	clkFreq = _clkFreq;
    
	widthNmos = MIN_NMOS_SIZE * tech.featureSize;
	widthPmos = tech.pnSizeRatio * MIN_NMOS_SIZE * tech.featureSize;

	initialized = true;
}

void VoltageSenseAmp::CalculateUnitArea() {
	if (!initialized) {
		cout << "[VoltageSenseAmp] Error: Require initialization first!" << endl;
	} else {
		double hNmos, wNmos, hPmos, wPmos;
		
		CalculateGateArea(INV, 1, widthNmos, 0, tech.featureSize*MAX_TRANSISTOR_HEIGHT, tech, &hNmos, &wNmos);
		CalculateGateArea(INV, 1, widthPmos, 0, tech.featureSize*MAX_TRANSISTOR_HEIGHT, tech, &hPmos, &wPmos);
		
		areaUnit = (hNmos * wNmos) * 6 + (hPmos * wPmos) * 5;
		
		// Resistance
		resPrecharge = CalculateOnResistance(widthPmos, PMOS, inputParameter.temperature, tech);
		// Capacitance
		CalculateGateCapacitance(INV, 1, widthNmos, 0, hNmos, tech, &capNmosGate, &capNmosDrain);
		CalculateGateCapacitance(INV, 1, widthPmos, 0, hPmos, tech, &capPmosGate, &capPmosDrain);

		capS1 = capNmosGate + capNmosDrain + capPmosDrain;
	}
}

void VoltageSenseAmp::CalculateArea(double _widthVoltageSenseAmp) {	// Just add up the area of all the components
	if (!initialized) {
		cout << "[VoltageSenseAmp] Error: Require initialization first!" << endl;
	} else {
		widthVoltageSenseAmp = _widthVoltageSenseAmp;

		double x = sqrt(areaUnit/HEIGHT_WIDTH_RATIO_LIMIT); // area = HEIGHT_WIDTH_RATIO_LIMIT * x^2
		if (widthVoltageSenseAmp > x)   // Limit W/H <= HEIGHT_WIDTH_RATIO_LIMIT
			widthVoltageSenseAmp = x;
		
		area = areaUnit * numReadCol;
		width = widthVoltageSenseAmp * numReadCol;
		height = areaUnit/widthVoltageSenseAmp;
	}
}

void VoltageSenseAmp::CalculateLatency(double capInputLoad, double numRead) {
	if (!initialized) {
		cout << "[VoltageSenseAmp] Error: Require initialization first!" << endl;
	} else {
		readLatency = 0;
		readLatency += 2.3 * resPrecharge * capS1 + voltageSenseDiff * (capS1 + capNmosDrain + capInputLoad) / (cell.readVoltage/cell.resMemCellOn - cell.readVoltage/cell.resMemCellOff);
		readLatency += 1/clkFreq * 2;	// Clock time for precharge and S/A enable
		
		readLatency *= numRead;
	}
}

void VoltageSenseAmp::CalculatePower(double numRead) {
	if (!initialized) {
		cout << "[VoltageSenseAmp] Error: Require initialization first!" << endl;
	} else {
		leakage = 0;

		// Leakage (assume connection to the cell is floating, no leakage on the precharge side, but in S/A it's roughly like 2 NAND2)
		leakage += CalculateGateLeakage(NAND, 2, widthNmos, widthPmos, inputParameter.temperature, tech) * tech.vdd * 2;
		
		// Dynamic energy
		readDynamicEnergy = 9.845e-15 * (tech.vdd / 1.1) * (tech.vdd / 1.1);	// 65nm tech node from SPICE simulation
		readDynamicEnergy *= numReadCol;
		
		readDynamicEnergy *= numRead;
		if (!readLatency) {
			//cout << "[VoltageSenseAmp] Error: Need to calculate read latency first" << endl;
		} else {
			readPower = readDynamicEnergy/readLatency;
		}

	}
}

void VoltageSenseAmp::PrintProperty(const char* str) {
	FunctionUnit::PrintProperty(str);
}

